package my.mall.admin.api.service

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
import my.mall.admin.api.enums.CacheKeyConst
import my.mall.admin.api.pojo.dict.DictReq
import my.mall.admin.api.pojo.dict.DictResp
import my.mall.admin.api.pojo.dict.DictTreeResp
import my.mall.common.enums.DeletedFlagEnum
import my.mall.common.enums.RespMetaEnum
import my.mall.common.exception.WebBaseException
import my.mall.common.pojo.CommonResp
import my.mall.common.pojo.PageParam
import my.mall.common.pojo.PageResp
import my.mall.common.util.RespUtils
import my.mall.db.entity.Dictionary
import my.mall.db.mapper.DictionaryMapper
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.util.CollectionUtils
import org.springframework.util.StringUtils
import java.util.stream.Collectors

@Service
open class DictionaryService(
    private val dictionaryMapper: DictionaryMapper
) : ServiceImpl<DictionaryMapper, Dictionary>() {

    open fun getDictList(dictNameList: List<String>): CommonResp<List<DictTreeResp>> {
        val pDictList = dictionaryMapper.selectList(
            QueryWrapper<Dictionary>()
                .select(
                    Dictionary.ID,
                    Dictionary.DICT_KEY,
                    Dictionary.DICT_VALUE,
                )
                .`in`(Dictionary.DICT_KEY, dictNameList)
                .eq(Dictionary.PARENT_ID, -1)
        )
        val pidList = pDictList.stream()
            .map(Dictionary::id)
            .collect(Collectors.toList())
        if (pidList.isEmpty()) {
            throw WebBaseException(RespMetaEnum.PARAM_ERROR)
        }
        val dictList = dictionaryMapper.selectList(
            QueryWrapper<Dictionary>()
                .select(
                    Dictionary.DICT_KEY,
                    Dictionary.DICT_VALUE,
                    Dictionary.PARENT_ID,
                )
                .`in`(Dictionary.PARENT_ID, pidList)
        )
        val dictMap = dictList.stream().collect(Collectors.groupingBy { it.parentId })
        val respList = mutableListOf<DictTreeResp>()
        for (pDict in pDictList) {
            val resp = DictTreeResp(pDict.id!!, pDict.dictKey!!, pDict.dictValue!!)
            val childList = dictMap[pDict.id]
            if (!CollectionUtils.isEmpty(childList)) {
                resp.childList = childList!!.stream().map {
                    DictTreeResp(it.id!!, it.dictKey!!, it.dictValue!!)
                }.collect(Collectors.toList())
            }
            respList.add(resp)
        }
        return RespUtils.success(respList)
    }

    open fun getParentDictList(name: String?, pageParam: PageParam): CommonResp<PageResp<DictResp>> {
        var page = Page<Dictionary>(pageParam.pageNum, pageParam.pageSize)
        page = dictionaryMapper.selectPage(page,
            QueryWrapper<Dictionary>()
                .select(
                    Dictionary.ID,
                    Dictionary.DICT_KEY,
                    Dictionary.DICT_VALUE,
                    Dictionary.DICT_ORDER,
                )
                .eq(Dictionary.PARENT_ID, -1)
                .eq(Dictionary.DELETED_FLAG, DeletedFlagEnum.NORMAL.value)
                .like(StringUtils.hasLength(name), Dictionary.DICT_VALUE, name)
                .orderByDesc(Dictionary.ID)
        )
        val pDictList = page.records
        val respList = pDictList.stream().map {
            DictResp(it.id!!, it.dictKey!!, it.dictValue!!, it.dictOrder!!)
        }.collect(Collectors.toList())
        return RespUtils.success(pageParam.pageNum, pageParam.pageSize, page.total, respList)
    }

    @Cacheable(cacheNames = [CacheKeyConst.DICT_BY_PID], key = "#pid")
    open fun getListByPid(pid: Int): CommonResp<List<DictResp>> {
        val dictList = dictionaryMapper.selectList(
            QueryWrapper<Dictionary>()
                .select(
                    Dictionary.ID,
                    Dictionary.DICT_KEY,
                    Dictionary.DICT_VALUE,
                    Dictionary.DICT_ORDER,
                )
                .eq(Dictionary.PARENT_ID, pid)
                .eq(Dictionary.DELETED_FLAG, DeletedFlagEnum.NORMAL.value)
        )
        val respList = dictList.stream().map {
            DictResp(it.id!!, it.dictKey!!, it.dictValue!!, it.dictOrder!!)
        }.collect(Collectors.toList())
        return RespUtils.success(respList)
    }

    open fun deleteDict(id: Int) {
        dictionaryMapper.update(UpdateWrapper<Dictionary>()
            .set(Dictionary.DELETED_FLAG, DeletedFlagEnum.DELETED.value)
            .eq(Dictionary.ID, id))
    }

    @Transactional(rollbackFor = [Exception::class])
    open fun saveDict(dict: DictReq) {
        val deleteList = ArrayList<Int>()
        val insertList = ArrayList<Dictionary>()
        val updateList = ArrayList<Dictionary>()
        val parent = getParentDict(dict)
        if (dict.id == null) {
            dictionaryMapper.insert(parent)
            val childrenList = dict.childrenList
            if (!CollectionUtils.isEmpty(childrenList)) {
                for (child in childrenList!!) {
                    val childDict = getChildrenDict(child, parent.id!!)
                    insertList.add(childDict)
                }
            }
        } else {
            updateList.add(parent)
            val childrenList = dict.childrenList
            val oldChildIds = dictionaryMapper.selectList(
                QueryWrapper<Dictionary>()
                    .select(
                        Dictionary.ID
                    )
                    .eq(Dictionary.PARENT_ID, dict.id)
            ).stream().map { it.id }.collect(Collectors.toList())
            if (!CollectionUtils.isEmpty(childrenList)) {
                val newChildIds = HashSet<Int>()
                for (child in childrenList!!) {
                    if (child.id == null) {
                        insertList.add(getChildrenDict(child, parent.id!!))
                    } else if (oldChildIds.contains(child.id)) {
                        newChildIds.add(child.id)
                        updateList.add(getChildrenDict(child, parent.id!!))
                    }
                }
                for (oldChildId in oldChildIds) {
                    if (!newChildIds.contains(oldChildId)) {
                        deleteList.add(oldChildId!!)
                    }
                }
            }
        }
        if (!CollectionUtils.isEmpty(deleteList)) {
            dictionaryMapper.update(UpdateWrapper<Dictionary>()
                .set(Dictionary.DELETED_FLAG, DeletedFlagEnum.DELETED.value)
                .`in`(Dictionary.ID, deleteList)
            )
        }
        if (!CollectionUtils.isEmpty(insertList)) {
            saveBatch(insertList)
        }
        if (!CollectionUtils.isEmpty(updateList)) {
            for (child in updateList) {
                dictionaryMapper.updateById(child)
            }
        }
    }

    private fun getParentDict(dict: DictReq): Dictionary {
        val parent = Dictionary()
        parent.id = dict.id
        parent.dictKey = dict.dictKey
        parent.dictValue = dict.dictValue
        parent.parentId = -1
        return parent
    }

    private fun getChildrenDict(dict: DictReq, pid: Int): Dictionary {
        val childDict = Dictionary()
        childDict.id = dict.id
        childDict.dictKey = dict.dictKey
        childDict.dictValue = dict.dictValue
        childDict.dictOrder = dict.dictOrder
        childDict.parentId = pid
        return childDict
    }
}
